package com.flute.framework.filesystem;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import com.flute.framework.FluteMonitor;
import com.flute.framework.exception.FileBlockException;
import com.flute.framework.monitor.EventLogger;
import com.flute.framework.util.ftp.IFileOperation;
import com.flute.framework.util.ftp.LocalFile;
import com.flute.tools.util.ExceptionUtil;

/**
 * 同时只能一次写操作，或者多个读操作
 * 
 * 将读写操作锁持久化，使得中断后能恢复
 * 
 */
public class FileDataBlock {

	private String path;
	private IFileOperation oper;
	private static final String WRITE = "filewrite";
	private static final String READ = "fileread";
	private static final String CLEAR = "fileclear";
	private String logKey;

	public FileDataBlock(String path, IFileOperation oper) {
		this.path = path;
		this.oper = oper;
	}

	public FileDataBlock(String path, String logKey) {
		this(path, logKey, new LocalFile());
	}

	public FileDataBlock(String path, String logKey, IFileOperation oper) {
		this.path = path;
		this.logKey = logKey;
		this.oper = oper;
	}

	public String getLogKey() {
		return logKey;
	}

	public void setLogKey(String logKey) {
		this.logKey = logKey;
	}

	public void setPath(String path) {
		this.path = path;
	}

	public IFileOperation getOper() {
		return oper;
	}

	public void setOper(IFileOperation oper) {
		this.oper = oper;
	}

	public boolean startWrite() {
		return new FileLock(WRITE, oper).create(path);
	}

	public boolean endWrite() {
		return new FileLock(WRITE, oper).delete(path);
	}

	public FileLock startRead() {
		return getReadLock();
	}

	public boolean endRead(FileLock lock) {
		return lock.delete(path);
	}

	public boolean create() {
		File file = new File(path);
		return file.mkdirs();
	}

	public boolean existed() {
		return new File(path).exists();
	}

	public boolean delete() {
		/*
		 * clear(); return new File(path).delete();
		 */
		clear();
		try {
			return oper.delete(path);
		} catch (Exception e) {
			EventLogger.error(e.getMessage(), FluteMonitor.getSearcherKey());
			return Boolean.FALSE;
		}
	}

	public boolean clear() {
		EventLogger.info("清空文件夹'" + path + "'...", logKey);
		if (!new FileLock(CLEAR, oper).create(path)) {
			return Boolean.FALSE;
		}
		/*
		 * if (!FileUtil.clearFolder(path, new FileLock(CLEAR).getName())) {
		 * return false; }
		 */
		if (!oper.clearFolder(path, new FileLock(CLEAR, oper).getName())) {
			return Boolean.FALSE;
		}
		if (!new FileLock(CLEAR, oper).delete(path)) {
			return Boolean.FALSE;
		}
		EventLogger.info("清空文件夹'" + path + "'结束.", logKey);

		return Boolean.TRUE;
	}

	public boolean isClearing() {
		return new FileLock(CLEAR, oper).existed(path);
	}

	public boolean isEmpty() {
		return getFiles().length == 0;
	}

	public boolean initialCheck() {
		EventLogger.info("正在检测文件夹'" + path + "'...", logKey);
		if (isWriting()) {
			EventLogger.warn("文件夹'" + path + "'在写操作时中断，删除所有文件", logKey);
			/* return FileUtil.clearFolder(path); */
			try {
				return oper.removeDirectory(path);
			} catch (Exception e) {
				EventLogger
						.error(e.getMessage(), FluteMonitor.getSearcherKey());
				return Boolean.FALSE;
			}
		} else if (isReading()) {
			EventLogger.warn("文件夹'" + path + "'在读操作时中断，删除锁文件", logKey);
			return removeLocks();
		} else if (isClearing()) {
			EventLogger.warn("文件夹'" + path + "'在做清除操作时中断，删除所有文件", logKey);
			/* return FileUtil.clearFolder(path); */
			try {
				return oper.removeDirectory(path);
			} catch (Exception e) {
				EventLogger
						.error(e.getMessage(), FluteMonitor.getSearcherKey());
				return Boolean.FALSE;
			}
		}
		EventLogger.info("文件夹'" + path + "'正常.", logKey);

		return Boolean.TRUE;
	}

	public String getPath() {
		if (!path.endsWith("/")) {
			path = path + "/";
		}

		return path;
	}

	public void copyFrom(FileDataBlock block) throws FileBlockException {
		copyFrom(block, Boolean.FALSE);
	}

	public void copyFrom(FileDataBlock block, boolean append)
			throws FileBlockException {
		EventLogger.info("复制文件：从文件夹'" + block.path + "'到文件夹'" + path + "'...",
				logKey);
		if (!append) {
			if (!clear()) {
				EventLogger.error("清除文件夹'" + path + "'下的文件失败.", logKey);
				throw new FileBlockException("清除文件夹'" + path + "'下的文件失败.");
			}
		}

		if (!startWrite()) {
			throw new FileBlockException("在文件夹'" + path + "'创建写锁失败.");
		}

		FileLock lock = block.getReadLock();

		/*
		 * for (File file : block.getFiles()) { try {
		 * FileUtil.copyFile(file.getAbsolutePath(), getPath() +
		 * file.getName()); } catch (IOException e) { throw new
		 * FileBlockException("复制文件'" + file.getAbsolutePath() + "'失败.\n" +
		 * ExceptionUtil.getExceptionContent(e)); } }
		 */
		IFileOperation oper = block.getOper();
		for (String file : block.getFiles()) {
			try {
				String fileName = oper.combin(getPath(), file);
				oper.download(file, fileName);
			} catch (Exception e) {
				throw new FileBlockException("复制文件'" + file + "'失败.\n"
						+ ExceptionUtil.getExceptionContent(e));
			}
		}
		if (!block.endRead(lock)) {
			throw new FileBlockException("在文件夹'" + block.path + "'删除读锁失败.");
		}

		if (!endWrite()) {
			throw new FileBlockException("在文件夹'" + path + "'删除写锁失败.");
		}

		EventLogger.info("文件复制成功.", logKey);
	}

	private FileLock getReadLock() {
		FileLock lock = null;

		while (Boolean.TRUE) {
			lock = new FileLock(String.valueOf(new Date().getTime()) + READ,
					oper);
			if (!lock.existed(path)) {
				if (lock.create(path)) {
					break;
				}
			}
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}

		return lock;
	}

	public boolean isReading() {
		/*
		 * File[] files = new File(path).listFiles(); if (files != null) { for
		 * (File file : files) { if (file.getName().endsWith(READ + "." +
		 * FileLock.LOCK)) { return true; } } }
		 * 
		 * return false;
		 */
		String[] files = null;
		try {
			files = oper.list(path);
			if (files != null) {
				for (int i = 0; i < files.length; i++) {
					if (files[i].endsWith(READ + "." + FileLock.LOCK)) {
						return true;
					}
				}
			}
		} catch (Exception e) {
			EventLogger.error(e.getMessage(), FluteMonitor.getSearcherKey());
		}
		return false;
	}

	public boolean isWriting() {
		return new FileLock(WRITE, oper).existed(path);
	}

	/**
	 * 返回修改时间最晚的文件的修改时间
	 * 
	 * @return
	 */
	public long lastModified() {
		
		long max = 0;
		for (String file : getFiles()) {
			long last = 0;
			try {
				last = oper.lastModified(file);
			} catch (IOException e) {
			}
			if (last > max) {
				max = last;
			}
		}

		return max;
	}

	public boolean removeReadLock(String fileName) {
		/* return FileUtil.deleteFile(path, fileName); */
		try {
			return oper.delete(path, fileName);
		} catch (Exception e) {
			return false;
		}
	}

	public List<String> getAllReadFiles() {
		List<String> list = new ArrayList<String>();

		String[] paths = null;
		try {
			paths = oper.list(path);
		} catch (Exception e) {
			EventLogger.error(e.getMessage(), FluteMonitor.getSearcherKey());
		}
		if (paths != null) {
			for (String path : paths) {
				list.add(path);
			}
		}
		return list;
	}

	public long getFilesSize() {
		
		long space = 0;
		for (String file : getFiles()) {
			try {
				space = space + oper.size(file);
			} catch (Exception e) {
				EventLogger
						.error(e.getMessage(), FluteMonitor.getSearcherKey());
			}
		}
		return space;
	}

	public String[] getFiles() {
		
		List<String> fileList = new ArrayList<String>();
		try {
			String[] files = oper.list(path);
			if (files != null) {
				for (String file : files) {
					String absoluteFile = oper.combin(path, file);
					if (!FileLock.isLockFile(absoluteFile, oper)) {
						fileList.add(absoluteFile);
					}
				}
			}
		} catch (Exception e) {
			EventLogger.error(e.getMessage(), FluteMonitor.getSearcherKey());
		}
		return fileList.toArray(new String[fileList.size()]);
	}

	public boolean removeLocks() {
		
		try {
			String[] files = oper.list(path);
			if (files != null) {
				for (String file : files) {
					String absoluteFile = oper.combin(path, file);
					if (FileLock.isLockFile(absoluteFile, oper)) {
						if (!oper.delete(absoluteFile)) {
							return false;
						}
					}
				}
			}
			return true;
		} catch (Exception e) {
			return false;
		}
	}

	public boolean removeReadLocks() {
		
		try {
			String[] files = oper.list(path);
			if (files != null && files.length > 0) {
				for (String file : files) {
					String absoluteFile = oper.combin(path, file);
					if (FileLock.isLockFile(absoluteFile, oper)
							&& file.endsWith(READ + "." + FileLock.LOCK)) {
						if (!oper.delete(absoluteFile)) {
							return true;
						}
					}

				}
			}
		} catch (Exception e) {
			EventLogger.error(e.getMessage(), FluteMonitor.getSearcherKey());
		}
		return false;
	}
}
